package flib;

import java.io.File;
import java.util.Arrays;
import java.util.TreeMap;

import processing.core.PApplet;
import processing.core.PImage;
import simpleGeom.DepthAnalyser2D;

public class ImageCleaner {
	PApplet pA;
	PImage image;
	float[] values;
	float[] distances;
	boolean[] selection;
	public PImage resultImage;
	int nX, nY;


	public ImageCleaner(PApplet pA, PImage image) {
		this.pA=pA;
		this.nX=image.width;
		this.nY=image.height;
		values = new float[image.pixels.length];
		distances = new float[image.pixels.length];
		selection=new boolean[image.pixels.length];
		image.loadPixels();
		for (int i = 0; i < image.pixels.length; i++) {
			values[i] = pA.brightness(image.pixels[i]);
		}
		//resultImage = pA.createImage(image.width, image.height, PApplet.RGB);
		//resultImage.updatePixels();
	}

	public PImage renderResult() {
		resultImage = pA.createImage(nX, nY, PApplet.RGB);
		resultImage.loadPixels();
		for (int i=0;i<values.length;i++){
			resultImage.pixels[i]=pA.color(values[i]);
		}
		resultImage.updatePixels();
		return resultImage;
	}
	public void contrast(float iso) {
		contrast(iso,values);
	}
	public void contrast(float iso, float[] values) {
		for (int i = 0; i < values.length; i++) {
			if (values[i] < iso)
				values[i] = 0;
			else {
				values[i] = 255;
			}
		}
	}

	public void selectContinous(int px, int py, int contrast) {
		removeSelection();
		int j = getIndex(px, py);
		float v = values[0];
		int[] candidates = new int[values.length];
		int[] newCandidates = new int[values.length];
		Arrays.fill(selection, false);
		int x1 = 0;
		int y1 = 0;
		int x2 = nX;
		int y2 = nY;
		int iBlob = 0;

		if (selection[j] == false) {
			selection[j] = true;
			Arrays.fill(candidates, 0);
			candidates[0] = j;
			Arrays.fill(newCandidates, 0);
			int nNew = 1;
			int size = 0;
			while (nNew > 0) {
				int nCandidates = nNew;
				nNew = 0;
				for (int i = 0; i < nCandidates; i++) {
					int cand = candidates[i];
					int x = (int) (cand * 1f % nX);
					int y = (int) (cand * 1f / nX);
					int cI = cand - 1;
					if (x > x1 && Math.abs(values[cI] - v) < contrast
							&& selection[cI] == false) {
						selection[cI] = true;
						newCandidates[nNew] = cI;
						nNew++;
					}
					cI = cand + 1;
					if (x < x2 - 1 && Math.abs(values[cI] - v) < contrast
							&& selection[cI] == false) {
						selection[cI] = true;
						newCandidates[nNew] = cI;
						nNew++;
					}
					cI = cand + nX;
					if (y < y2 - 1 && Math.abs(values[cI] - v) < contrast
							&& selection[cI] == false) {
						selection[cI] = true;
						newCandidates[nNew] = cI;
						nNew++;
					}
					cI = cand - nX;

					if (y > y1 && Math.abs(values[cI] - v) < contrast
							&& selection[cI] == false) {
						selection[cI] = true;
						newCandidates[nNew] = cI;
						nNew++;
					}
				}
				size += nNew;
				for (int i = 0; i < nNew; i++) {
					candidates[i] = newCandidates[i];
				}
			}
			iBlob++;
		}

	}
	public void selectColor(float c) {
		for (int i = 0; i < values.length; i++) {
			if (values[i] == c) {
				selection[i] = true;
			} else {
				selection[i] = false;
			}

		}
	}
	public void removeSelection(){
		Arrays.fill(selection, false);
	}
	public void offsetSelectionBox(int offset) {
		int mx=nX*nY;
		for (int i = 0; i < selection.length; i++) {
			if (selection[i]) {
				distances[i] = 0;
			} else {
				distances[i] = mx;
			}
		}
		for (int i=0;i<distances.length;i++){
			if (distances[i] == 0){
				int cX=getX(i);
				int cY=getY(i);
				int x1=Math.max(cX-offset,0);
				int y1=Math.max(cY-offset,0);
				int x2=Math.min(cX+offset,nX-1);
				int y2=Math.min(cY+offset,nY-1);
				for (int x=x1;x<=x2;x++){
					for (int y=y1;y<=y2;y++){
						selection[getIndex(x,y)]=true;
						//int index=
					}
				}
			}
		}
		
	}
	public int getX(int index){
		return index%nX;
	}
	public int getY(int index){
		return index/nX;
	}
	public int getIndex(int x,int y){
		return y*nX+x;
	}
	public void offsetSelection(float offset) {
		int mx=nX*nY;
		for (int i = 0; i < selection.length; i++) {
			if (selection[i]) {
				distances[i] = 0;
			} else {
				distances[i] =mx;
			}
		}
		DepthAnalyser2D.calculateDepth(distances, nY, nX, 1, 1.141f);
		for (int i = 0; i < selection.length; i++) {
			if (distances[i] < offset) {
				selection[i] = true;
			}
		}
	}

	public void invertSelection() {
		for (int i = 0; i < selection.length; i++) {
			selection[i] = !selection[i];
		}
	}
	public void booleanAddSelection(boolean[]values){
		for (int i = 0; i < values.length; i++) {
			if (values[i]){
			selection[i]=true;
			}
		}
	}
	public void booleanSubSelection(boolean[]values){
		for (int i = 0; i < values.length; i++) {
			if (values[i]){
			selection[i]=false;
			}
		}
	}
	public void booleanIntersectSelection(boolean[]values){
		for (int i = 0; i < values.length; i++) {
			if (!values[i]||!selection[i]){
				selection[i]=false;
			}
		}
	}
	public void fillSelection(float c) {
		for (int i = 0; i < selection.length; i++) {
			if (selection[i]) {
				values[i] = c;
			}
		}
	}
	public boolean[] getCopySelection() {
		return Arrays.copyOf(selection,selection.length);
	}
	
	public int[][] blobs(boolean[] solids) {
		int[] blobs=new int[solids.length];
		int[] candidates=new int[solids.length];
		int[] newCandidates=new int[solids.length];
		Arrays.fill(blobs,-1);
		int x1 = 0;
		int y1 = 0;
		int x2 = nX;
		int y2 = nY;
		int iBlob = 0;
		for (int j = 0; j < solids.length; j++) {
			if (blobs[j] == -1) {
				blobs[j] = iBlob;
				boolean cCol = solids[j];
				Arrays.fill(candidates, 0);
				candidates[0]=j;
				Arrays.fill(newCandidates, 0);
				int nNew = 1;
				int size=0;
				while (nNew > 0) {
					int nCandidates = nNew;
					nNew = 0;
					for (int i = 0; i < nCandidates; i++) {
						int cand = candidates[i];
						int x = (int) (cand * 1f % nX);
						int y = (int) (cand * 1f / nX);
						int leftI = cand - 1;
						if (x > x1 && solids[leftI] == cCol
								&& blobs[leftI] == -1) {
							blobs[leftI] = iBlob;
							newCandidates[nNew] = leftI;
							nNew++;
						}
						int rightI = cand + 1;
						if (x < x2 - 1 && solids[rightI] == cCol
								&& blobs[rightI] == -1) {
							blobs[rightI] = iBlob;
							newCandidates[nNew] = rightI;
							nNew++;
						}
						int topI = cand + nX;
						if (y < y2 - 1 && solids[topI] == cCol
								&& blobs[topI] == -1) {
							blobs[topI] = iBlob;
							newCandidates[nNew] = topI;
							nNew++;
						}
						int bottomI = cand - nX;
						
						if (y > y1  && solids[bottomI] == cCol
								&& blobs[bottomI] == -1) {
							blobs[bottomI] = iBlob;
							newCandidates[nNew] = bottomI;
							nNew++;
						}
					}
					size+=nNew;
					for (int i=0;i<nNew;i++){
						candidates[i]=newCandidates[i];
					}
				}
				iBlob++;
			}
		}
		
		iBlob++;
		int[][]blobInfo=new int[iBlob][5];
		for (int i=0;i<blobInfo.length;i++){
			blobInfo[i][0]=1000000;
			blobInfo[i][1]=1000000;
			blobInfo[i][2]=-1000000;
			blobInfo[i][3]=-1000000;
		}
		
		for (int i=0;i<blobs.length;i++){
			int iBl=blobs[i];
			int x = (int) (i * 1f % nX);
			int y = (int) (i * 1f / nX);
			blobInfo[iBl][0]=Math.min(blobInfo[iBl][0],x);
			blobInfo[iBl][1]=Math.min(blobInfo[iBl][1],y);
			blobInfo[iBl][2]=Math.max(blobInfo[iBl][2],x);
			blobInfo[iBl][3]=Math.max(blobInfo[iBl][3],y);
			if (solids[i]){
				blobInfo[iBl][4]=1;
			}
		}
		return blobInfo;
	}

	public boolean[] getSelection() {
		return selection;
	}

	public void setSelection(boolean[] selection) {
		this.selection = selection;
	}
	public void loadSelection(PImage img){
		for (int i=0;i<selection.length;i++){
			if (selection[i]){
				img.pixels[i]=pA.color(255,0,0,100);
			}
			else{
				img.pixels[i]=pA.color(255,255);
			}
		}
	}
}
